//-----------------------------------------------------------------------------
// File: DXUtil.cpp
//
// Desc: Shortcut macros and functions for using DX objects
//
// Copyright (c) Microsoft Corporation. All rights reserved
//-----------------------------------------------------------------------------
#ifndef STRICT
#define STRICT
#endif // !STRICT
#include <windows.h>
#include <mmsystem.h>
#include <tchar.h>
#include <stdio.h> 
#include <stdarg.h>
#include "DXUtil.h"


#ifdef UNICODE
typedef HINSTANCE (WINAPI* LPShellExecute)(HWND hwnd, LPCWSTR lpOperation, LPCWSTR lpFile, LPCWSTR lpParameters, LPCWSTR lpDirectory, INT nShowCmd);
#else
typedef HINSTANCE (WINAPI* LPShellExecute)(HWND hwnd, LPCSTR lpOperation, LPCSTR lpFile, LPCSTR lpParameters, LPCSTR lpDirectory, INT nShowCmd);
#endif


#ifndef UNDER_CE
//-----------------------------------------------------------------------------
// Name: DXUtil_GetDXSDKMediaPathCch()
// Desc: Returns the DirectX SDK media path
//       cchDest is the size in TCHARs of strDest.  Be careful not to 
//       pass in sizeof(strDest) on UNICODE builds.
//-----------------------------------------------------------------------------
HRESULT DXUtil_GetDXSDKMediaPathCch( TCHAR* strDest, int cchDest )
{
	if( strDest == NULL || cchDest < 1 )
		return E_INVALIDARG;

	lstrcpy( strDest, TEXT("") );

	// Open the appropriate registry key
	HKEY  hKey;
	LONG lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
		_T("Software\\Microsoft\\DirectX SDK"),
		0, KEY_READ, &hKey );
	if( ERROR_SUCCESS != lResult )
		return E_FAIL;

	DWORD dwType;
	DWORD dwSize = cchDest * sizeof(TCHAR);
	lResult = RegQueryValueEx( hKey, _T("DX9SDK Samples Path"), NULL,
		&dwType, (BYTE*)strDest, &dwSize );
	strDest[cchDest-1] = 0; // RegQueryValueEx doesn't NULL term if buffer too small
	RegCloseKey( hKey );

	if( ERROR_SUCCESS != lResult )
		return E_FAIL;

	const TCHAR* strMedia = _T("\\Media\\");
	if( lstrlen(strDest) + lstrlen(strMedia) < cchDest )
		_tcscat( strDest, strMedia );
	else
		return E_INVALIDARG;

	return S_OK;
}
#endif // !UNDER_CE



#ifndef UNDER_CE
//-----------------------------------------------------------------------------
// Name: DXUtil_FindMediaFileCch()
// Desc: Returns a valid path to a DXSDK media file
//       cchDest is the size in TCHARs of strDestPath.  Be careful not to 
//       pass in sizeof(strDest) on UNICODE builds.
//-----------------------------------------------------------------------------
HRESULT DXUtil_FindMediaFileCch( TCHAR* strDestPath, int cchDest, TCHAR* strFilename )
{
	HRESULT hr;
	HANDLE file;
	TCHAR* strShortNameTmp = NULL;
	TCHAR strShortName[MAX_PATH];
	int cchPath;

	if( NULL==strFilename || NULL==strDestPath || cchDest < 1 )
		return E_INVALIDARG;

	lstrcpy( strDestPath, TEXT("") );
	lstrcpy( strShortName, TEXT("") );

	// Build full path name from strFileName (strShortName will be just the leaf filename)
	cchPath = GetFullPathName(strFilename, cchDest, strDestPath, &strShortNameTmp);
	if ((cchPath == 0) || (cchDest <= cchPath))
		return E_FAIL;
	if( strShortNameTmp )
		lstrcpyn( strShortName, strShortNameTmp, MAX_PATH );

	// first try to find the filename given a full path
	file = CreateFile( strDestPath, GENERIC_READ, FILE_SHARE_READ, NULL, 
		OPEN_EXISTING, 0, NULL );
	if( INVALID_HANDLE_VALUE != file )
	{
		CloseHandle( file );
		return S_OK;
	}

	// next try to find the filename in the current working directory (path stripped)
	file = CreateFile( strShortName, GENERIC_READ, FILE_SHARE_READ, NULL, 
		OPEN_EXISTING, 0, NULL );
	if( INVALID_HANDLE_VALUE != file )
	{
		_tcsncpy( strDestPath, strShortName, cchDest );
		strDestPath[cchDest-1] = 0; // _tcsncpy doesn't NULL term if it runs out of space
		CloseHandle( file );
		return S_OK;
	}

	// last, check if the file exists in the media directory
	if( FAILED( hr = DXUtil_GetDXSDKMediaPathCch( strDestPath, cchDest ) ) )
		return hr;

	if( lstrlen(strDestPath) + lstrlen(strShortName) < cchDest )
		lstrcat( strDestPath, strShortName );
	else
		return E_INVALIDARG;

	file = CreateFile( strDestPath, GENERIC_READ, FILE_SHARE_READ, NULL, 
		OPEN_EXISTING, 0, NULL );
	if( INVALID_HANDLE_VALUE != file )
	{
		CloseHandle( file );
		return S_OK;
	}

	// On failure, just return the file as the path
	_tcsncpy( strDestPath, strFilename, cchDest );
	strDestPath[cchDest-1] = 0; // _tcsncpy doesn't NULL term if it runs out of space
	return HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
}
#endif // !UNDER_CE




//-----------------------------------------------------------------------------
// Name: DXUtil_ReadStringRegKeyCch()
// Desc: Helper function to read a registry key string
//       cchDest is the size in TCHARs of strDest.  Be careful not to 
//       pass in sizeof(strDest) on UNICODE builds.
//-----------------------------------------------------------------------------
HRESULT DXUtil_ReadStringRegKeyCch( HKEY hKey, TCHAR* strRegName, TCHAR* strDest, 
	DWORD cchDest, TCHAR* strDefault )
{
	DWORD dwType;
	DWORD cbDest = cchDest * sizeof(TCHAR);

	if( ERROR_SUCCESS != RegQueryValueEx( hKey, strRegName, 0, &dwType, 
		(BYTE*)strDest, &cbDest ) )
	{
		_tcsncpy( strDest, strDefault, cchDest );
		strDest[cchDest-1] = 0;

		if( dwType != REG_SZ )
			return E_FAIL;

		return S_OK;
	}

	return E_FAIL;
}




//-----------------------------------------------------------------------------
// Name: DXUtil_WriteStringRegKey()
// Desc: Helper function to write a registry key string
//-----------------------------------------------------------------------------
HRESULT DXUtil_WriteStringRegKey( HKEY hKey, TCHAR* strRegName,
	TCHAR* strValue )
{
	if( NULL == strValue )
		return E_INVALIDARG;

	DWORD cbValue = ((DWORD)_tcslen(strValue)+1) * sizeof(TCHAR);

	if( ERROR_SUCCESS != RegSetValueEx( hKey, strRegName, 0, REG_SZ, 
		(BYTE*)strValue, cbValue ) )
		return E_FAIL;

	return S_OK;
}




//-----------------------------------------------------------------------------
// Name: DXUtil_ReadIntRegKey()
// Desc: Helper function to read a registry key int
//-----------------------------------------------------------------------------
HRESULT DXUtil_ReadIntRegKey( HKEY hKey, TCHAR* strRegName, DWORD* pdwDest, 
	DWORD dwDefault )
{
	DWORD dwType;
	DWORD dwLength = sizeof(DWORD);

	if( ERROR_SUCCESS != RegQueryValueEx( hKey, strRegName, 0, &dwType, 
		(BYTE*)pdwDest, &dwLength ) )
	{
		*pdwDest = dwDefault;
		if( dwType != REG_DWORD )
			return E_FAIL;

		return S_OK;
	}

	return E_FAIL;
}




//-----------------------------------------------------------------------------
// Name: DXUtil_WriteIntRegKey()
// Desc: Helper function to write a registry key int
//-----------------------------------------------------------------------------
HRESULT DXUtil_WriteIntRegKey( HKEY hKey, TCHAR* strRegName, DWORD dwValue )
{
	if( ERROR_SUCCESS != RegSetValueEx( hKey, strRegName, 0, REG_DWORD, 
		(BYTE*)&dwValue, sizeof(DWORD) ) )
		return E_FAIL;

	return S_OK;
}




//-----------------------------------------------------------------------------
// Name: DXUtil_ReadBoolRegKey()
// Desc: Helper function to read a registry key BOOL
//-----------------------------------------------------------------------------
HRESULT DXUtil_ReadBoolRegKey( HKEY hKey, TCHAR* strRegName, BOOL* pbDest, 
	BOOL bDefault )
{
	DWORD dwType;
	DWORD dwLength = sizeof(BOOL);

	if( ERROR_SUCCESS != RegQueryValueEx( hKey, strRegName, 0, &dwType, 
		(BYTE*)pbDest, &dwLength ) )
	{
		*pbDest = bDefault;
		if( dwType != REG_DWORD )
			return E_FAIL;

		return S_OK;
	}

	return E_FAIL;
}




//-----------------------------------------------------------------------------
// Name: DXUtil_WriteBoolRegKey()
// Desc: Helper function to write a registry key BOOL
//-----------------------------------------------------------------------------
HRESULT DXUtil_WriteBoolRegKey( HKEY hKey, TCHAR* strRegName, BOOL bValue )
{
	if( ERROR_SUCCESS != RegSetValueEx( hKey, strRegName, 0, REG_DWORD, 
		(BYTE*)&bValue, sizeof(BOOL) ) )
		return E_FAIL;

	return S_OK;
}




//-----------------------------------------------------------------------------
// Name: DXUtil_ReadGuidRegKey()
// Desc: Helper function to read a registry key guid
//-----------------------------------------------------------------------------
HRESULT DXUtil_ReadGuidRegKey( HKEY hKey, TCHAR* strRegName, GUID* pGuidDest, 
	GUID& guidDefault )
{
	DWORD dwType;
	DWORD dwLength = sizeof(GUID);

	if( ERROR_SUCCESS != RegQueryValueEx( hKey, strRegName, 0, &dwType, 
		(LPBYTE) pGuidDest, &dwLength ) )
	{
		*pGuidDest = guidDefault;
		if( dwType != REG_BINARY )
			return E_FAIL;

		return S_OK;
	}

	return E_FAIL;
}




//-----------------------------------------------------------------------------
// Name: DXUtil_WriteGuidRegKey()
// Desc: Helper function to write a registry key guid
//-----------------------------------------------------------------------------
HRESULT DXUtil_WriteGuidRegKey( HKEY hKey, TCHAR* strRegName, GUID guidValue )
{
	if( ERROR_SUCCESS != RegSetValueEx( hKey, strRegName, 0, REG_BINARY, 
		(BYTE*)&guidValue, sizeof(GUID) ) )
		return E_FAIL;

	return S_OK;
}




//-----------------------------------------------------------------------------
// Name: DXUtil_Timer()
// Desc: Performs timer opertations. Use the following commands:
//          TIMER_RESET           - to reset the timer
//          TIMER_START           - to start the timer
//          TIMER_STOP            - to stop (or pause) the timer
//          TIMER_ADVANCE         - to advance the timer by 0.1 seconds
//          TIMER_GETABSOLUTETIME - to get the absolute system time
//          TIMER_GETAPPTIME      - to get the current time
//          TIMER_GETELAPSEDTIME  - to get the time that elapsed between 
//                                  TIMER_GETELAPSEDTIME calls
//-----------------------------------------------------------------------------
FLOAT __stdcall DXUtil_Timer( TIMER_COMMAND command )
{
	static BOOL     m_bTimerInitialized = FALSE;
	static BOOL     m_bUsingQPF         = FALSE;
	static BOOL     m_bTimerStopped     = TRUE;
	static LONGLONG m_llQPFTicksPerSec  = 0;

	// Initialize the timer
	if( FALSE == m_bTimerInitialized )
	{
		m_bTimerInitialized = TRUE;

		// Use QueryPerformanceFrequency() to get frequency of timer.  If QPF is
		// not supported, we will timeGetTime() which returns milliseconds.
		LARGE_INTEGER qwTicksPerSec;
		m_bUsingQPF = QueryPerformanceFrequency( &qwTicksPerSec );
		if( m_bUsingQPF )
			m_llQPFTicksPerSec = qwTicksPerSec.QuadPart;
	}

	if( m_bUsingQPF )
	{
		static LONGLONG m_llStopTime        = 0;
		static LONGLONG m_llLastElapsedTime = 0;
		static LONGLONG m_llBaseTime        = 0;
		double fTime;
		double fElapsedTime;
		LARGE_INTEGER qwTime;

		// Get either the current time or the stop time, depending
		// on whether we're stopped and what command was sent
		if( m_llStopTime != 0 && command != TIMER_START && command != TIMER_GETABSOLUTETIME)
			qwTime.QuadPart = m_llStopTime;
		else
			QueryPerformanceCounter( &qwTime );

		// Return the elapsed time
		if( command == TIMER_GETELAPSEDTIME )
		{
			fElapsedTime = (double) ( qwTime.QuadPart - m_llLastElapsedTime ) / (double) m_llQPFTicksPerSec;
			m_llLastElapsedTime = qwTime.QuadPart;
			return (FLOAT) fElapsedTime;
		}

		// Return the current time
		if( command == TIMER_GETAPPTIME )
		{
			double fAppTime = (double) ( qwTime.QuadPart - m_llBaseTime ) / (double) m_llQPFTicksPerSec;
			return (FLOAT) fAppTime;
		}

		// Reset the timer
		if( command == TIMER_RESET )
		{
			m_llBaseTime        = qwTime.QuadPart;
			m_llLastElapsedTime = qwTime.QuadPart;
			m_llStopTime        = 0;
			m_bTimerStopped     = FALSE;
			return 0.0f;
		}

		// Start the timer
		if( command == TIMER_START )
		{
			if( m_bTimerStopped )
				m_llBaseTime += qwTime.QuadPart - m_llStopTime;
			m_llStopTime = 0;
			m_llLastElapsedTime = qwTime.QuadPart;
			m_bTimerStopped = FALSE;
			return 0.0f;
		}

		// Stop the timer
		if( command == TIMER_STOP )
		{
			if( !m_bTimerStopped )
			{
				m_llStopTime = qwTime.QuadPart;
				m_llLastElapsedTime = qwTime.QuadPart;
				m_bTimerStopped = TRUE;
			}
			return 0.0f;
		}

		// Advance the timer by 1/10th second
		if( command == TIMER_ADVANCE )
		{
			m_llStopTime += m_llQPFTicksPerSec/10;
			return 0.0f;
		}

		if( command == TIMER_GETABSOLUTETIME )
		{
			fTime = qwTime.QuadPart / (double) m_llQPFTicksPerSec;
			return (FLOAT) fTime;
		}

		return -1.0f; // Invalid command specified
	}
	else
	{
		// Get the time using timeGetTime()
		static double m_fLastElapsedTime  = 0.0;
		static double m_fBaseTime         = 0.0;
		static double m_fStopTime         = 0.0;
		double fTime;
		double fElapsedTime;

		// Get either the current time or the stop time, depending
		// on whether we're stopped and what command was sent
		if( m_fStopTime != 0.0 && command != TIMER_START && command != TIMER_GETABSOLUTETIME)
			fTime = m_fStopTime;
		else
			fTime = GETTIMESTAMP() * 0.001;

		// Return the elapsed time
		if( command == TIMER_GETELAPSEDTIME )
		{   
			fElapsedTime = (double) (fTime - m_fLastElapsedTime);
			m_fLastElapsedTime = fTime;
			return (FLOAT) fElapsedTime;
		}

		// Return the current time
		if( command == TIMER_GETAPPTIME )
		{
			return (FLOAT) (fTime - m_fBaseTime);
		}

		// Reset the timer
		if( command == TIMER_RESET )
		{
			m_fBaseTime         = fTime;
			m_fLastElapsedTime  = fTime;
			m_fStopTime         = 0;
			m_bTimerStopped     = FALSE;
			return 0.0f;
		}

		// Start the timer
		if( command == TIMER_START )
		{
			if( m_bTimerStopped )
				m_fBaseTime += fTime - m_fStopTime;
			m_fStopTime = 0.0f;
			m_fLastElapsedTime  = fTime;
			m_bTimerStopped = FALSE;
			return 0.0f;
		}

		// Stop the timer
		if( command == TIMER_STOP )
		{
			if( !m_bTimerStopped )
			{
				m_fStopTime = fTime;
				m_fLastElapsedTime  = fTime;
				m_bTimerStopped = TRUE;
			}
			return 0.0f;
		}

		// Advance the timer by 1/10th second
		if( command == TIMER_ADVANCE )
		{
			m_fStopTime += 0.1f;
			return 0.0f;
		}

		if( command == TIMER_GETABSOLUTETIME )
		{
			return (FLOAT) fTime;
		}

		return -1.0f; // Invalid command specified
	}
}




//-----------------------------------------------------------------------------
// Name: DXUtil_ConvertAnsiStringToWideCch()
// Desc: This is a UNICODE conversion utility to convert a CHAR string into a
//       WCHAR string. 
//       cchDestChar is the size in TCHARs of wstrDestination.  Be careful not to 
//       pass in sizeof(strDest) 
//-----------------------------------------------------------------------------
HRESULT DXUtil_ConvertAnsiStringToWideCch( WCHAR* wstrDestination, const CHAR* strSource, 
	int cchDestChar )
{
	if( wstrDestination==NULL || strSource==NULL || cchDestChar < 1 )
		return E_INVALIDARG;

	int nResult = MultiByteToWideChar( CP_ACP, 0, strSource, -1, 
		wstrDestination, cchDestChar );
	wstrDestination[cchDestChar-1] = 0;

	if( nResult == 0 )
		return E_FAIL;
	return S_OK;
}




//-----------------------------------------------------------------------------
// Name: DXUtil_ConvertWideStringToAnsi()
// Desc: This is a UNICODE conversion utility to convert a WCHAR string into a
//       CHAR string. 
//       cchDestChar is the size in TCHARs of strDestination
//-----------------------------------------------------------------------------
HRESULT DXUtil_ConvertWideStringToAnsiCch( CHAR* strDestination, const WCHAR* wstrSource, 
	int cchDestChar )
{
	if( strDestination==NULL || wstrSource==NULL || cchDestChar < 1 )
		return E_INVALIDARG;

	int nResult = WideCharToMultiByte( CP_ACP, 0, wstrSource, -1, strDestination, 
		cchDestChar*sizeof(CHAR), NULL, NULL );
	strDestination[cchDestChar-1] = 0;

	if( nResult == 0 )
		return E_FAIL;
	return S_OK;
}




//-----------------------------------------------------------------------------
// Name: DXUtil_ConvertGenericStringToAnsi()
// Desc: This is a UNICODE conversion utility to convert a TCHAR string into a
//       CHAR string. 
//       cchDestChar is the size in TCHARs of strDestination
//-----------------------------------------------------------------------------
HRESULT DXUtil_ConvertGenericStringToAnsiCch( CHAR* strDestination, const TCHAR* tstrSource, 
	int cchDestChar )
{
	if( strDestination==NULL || tstrSource==NULL || cchDestChar < 1 )
		return E_INVALIDARG;

#ifdef _UNICODE
	return DXUtil_ConvertWideStringToAnsiCch( strDestination, tstrSource, cchDestChar );
#else
	strncpy( strDestination, tstrSource, cchDestChar );
	strDestination[cchDestChar-1] = '\0';
	return S_OK;
#endif   
}




//-----------------------------------------------------------------------------
// Name: DXUtil_ConvertGenericStringToWide()
// Desc: This is a UNICODE conversion utility to convert a TCHAR string into a
//       WCHAR string. 
//       cchDestChar is the size in TCHARs of wstrDestination.  Be careful not to 
//       pass in sizeof(strDest) 
//-----------------------------------------------------------------------------
HRESULT DXUtil_ConvertGenericStringToWideCch( WCHAR* wstrDestination, const TCHAR* tstrSource, 
	int cchDestChar )
{
	if( wstrDestination==NULL || tstrSource==NULL || cchDestChar < 1 )
		return E_INVALIDARG;

#ifdef _UNICODE
	wcsncpy( wstrDestination, tstrSource, cchDestChar );
	wstrDestination[cchDestChar-1] = L'\0';
	return S_OK;
#else
	return DXUtil_ConvertAnsiStringToWideCch( wstrDestination, tstrSource, cchDestChar );
#endif    
}




//-----------------------------------------------------------------------------
// Name: DXUtil_ConvertAnsiStringToGeneric()
// Desc: This is a UNICODE conversion utility to convert a CHAR string into a
//       TCHAR string. 
//       cchDestChar is the size in TCHARs of tstrDestination.  Be careful not to 
//       pass in sizeof(strDest) on UNICODE builds
//-----------------------------------------------------------------------------
HRESULT DXUtil_ConvertAnsiStringToGenericCch( TCHAR* tstrDestination, const CHAR* strSource, 
	int cchDestChar )
{
	if( tstrDestination==NULL || strSource==NULL || cchDestChar < 1 )
		return E_INVALIDARG;

#ifdef _UNICODE
	return DXUtil_ConvertAnsiStringToWideCch( tstrDestination, strSource, cchDestChar );
#else
	strncpy( tstrDestination, strSource, cchDestChar );
	tstrDestination[cchDestChar-1] = '\0';
	return S_OK;
#endif    
}




//-----------------------------------------------------------------------------
// Name: DXUtil_ConvertAnsiStringToGeneric()
// Desc: This is a UNICODE conversion utility to convert a WCHAR string into a
//       TCHAR string. 
//       cchDestChar is the size in TCHARs of tstrDestination.  Be careful not to 
//       pass in sizeof(strDest) on UNICODE builds
//-----------------------------------------------------------------------------
HRESULT DXUtil_ConvertWideStringToGenericCch( TCHAR* tstrDestination, const WCHAR* wstrSource, 
	int cchDestChar )
{
	if( tstrDestination==NULL || wstrSource==NULL || cchDestChar < 1 )
		return E_INVALIDARG;

#ifdef _UNICODE
	wcsncpy( tstrDestination, wstrSource, cchDestChar );
	tstrDestination[cchDestChar-1] = L'\0';    
	return S_OK;
#else
	return DXUtil_ConvertWideStringToAnsiCch( tstrDestination, wstrSource, cchDestChar );
#endif
}





//-----------------------------------------------------------------------------
// Name: DXUtil_LaunchReadme()
// Desc: Finds and opens the readme.txt for this sample
//-----------------------------------------------------------------------------
VOID DXUtil_LaunchReadme( HWND hWnd, TCHAR* strLoc )
{

#ifdef UNDER_CE
	// This is not available on PocketPC
	MessageBox( hWnd, TEXT("For operating instructions, please open the ")
		TEXT("readme.txt file included with the project."),
		TEXT("DirectX SDK Sample"), MB_ICONWARNING | MB_OK );

	return;
#else 

	bool bSuccess = false;
	bool bFound = false;
	TCHAR strReadmePath[1024];
	TCHAR strExeName[MAX_PATH];
	TCHAR strExePath[MAX_PATH];
	TCHAR strSamplePath[MAX_PATH];
	TCHAR* strLastSlash = NULL;

	lstrcpy( strReadmePath, TEXT("") );
	lstrcpy( strExePath, TEXT("") );
	lstrcpy( strExeName, TEXT("") );
	lstrcpy( strSamplePath, TEXT("") );

	// If the user provided a location for the readme, check there first.
	if( strLoc )
	{
		HKEY  hKey;
		LONG lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
			_T("Software\\Microsoft\\DirectX SDK"),
			0, KEY_READ, &hKey );
		if( ERROR_SUCCESS == lResult )
		{
			DWORD dwType;
			DWORD dwSize = MAX_PATH * sizeof(TCHAR);
			lResult = RegQueryValueEx( hKey, _T("DX9SDK Samples Path"), NULL,
				&dwType, (BYTE*)strSamplePath, &dwSize );
			strSamplePath[MAX_PATH-1] = 0; // RegQueryValueEx doesn't NULL term if buffer too small

			if( ERROR_SUCCESS == lResult )
			{
				_sntprintf( strReadmePath, 1023, TEXT("%s\\C++\\%s\\readme.txt"), 
					strSamplePath, strLoc );
				strReadmePath[1023] = 0;

				if( GetFileAttributes( strReadmePath ) != 0xFFFFFFFF )
					bFound = TRUE;
			}
		}

		RegCloseKey( hKey );
	}

	// Get the exe name, and exe path
	GetModuleFileName( NULL, strExePath, MAX_PATH );
	strExePath[MAX_PATH-1]=0;

	strLastSlash = _tcsrchr( strExePath, TEXT('\\') );
	if( strLastSlash )
	{
		_tcsncpy( strExeName, &strLastSlash[1], MAX_PATH );
		strExeName[MAX_PATH-1]=0;

		// Chop the exe name from the exe path
		*strLastSlash = 0;

		// Chop the .exe from the exe name
		strLastSlash = _tcsrchr( strExeName, TEXT('.') );
		if( strLastSlash )
			*strLastSlash = 0;
	}

	if( !bFound )
	{
		// Search in "%EXE_DIR%\..\%EXE_NAME%".  This matchs the DirectX SDK layout
		_tcscpy( strReadmePath, strExePath );

		strLastSlash = _tcsrchr( strReadmePath, TEXT('\\') );
		if( strLastSlash )
			*strLastSlash = 0;
		lstrcat( strReadmePath, TEXT("\\") );
		lstrcat( strReadmePath, strExeName );
		lstrcat( strReadmePath, TEXT("\\readme.txt") );
		if( GetFileAttributes( strReadmePath ) != 0xFFFFFFFF )
			bFound = TRUE;
	}

	if( !bFound )
	{
		// Search in "%EXE_DIR%\"
		_tcscpy( strReadmePath, strExePath );
		lstrcat( strReadmePath, TEXT("\\readme.txt") );
		if( GetFileAttributes( strReadmePath ) != 0xFFFFFFFF )
			bFound = TRUE;
	}

	if( !bFound )
	{
		// Search in "%EXE_DIR%\.."
		_tcscpy( strReadmePath, strExePath );
		strLastSlash = _tcsrchr( strReadmePath, TEXT('\\') );
		if( strLastSlash )
			*strLastSlash = 0;
		lstrcat( strReadmePath, TEXT("\\readme.txt") );
		if( GetFileAttributes( strReadmePath ) != 0xFFFFFFFF )
			bFound = TRUE;
	}

	if( !bFound )
	{
		// Search in "%EXE_DIR%\..\.."
		_tcscpy( strReadmePath, strExePath );
		strLastSlash = _tcsrchr( strReadmePath, TEXT('\\') );
		if( strLastSlash )
			*strLastSlash = 0;
		strLastSlash = _tcsrchr( strReadmePath, TEXT('\\') );
		if( strLastSlash )
			*strLastSlash = 0;
		lstrcat( strReadmePath, TEXT("\\readme.txt") );
		if( GetFileAttributes( strReadmePath ) != 0xFFFFFFFF )
			bFound = TRUE;
	}

	if( bFound )
	{
		// GetProcAddress for ShellExecute, so we don't have to include shell32.lib 
		// in every project that uses dxutil.cpp
		LPShellExecute pShellExecute = NULL;
		HINSTANCE hInstShell32 = LoadLibrary(TEXT("shell32.dll"));
		if (hInstShell32 != NULL)
		{
#ifdef UNICODE
			pShellExecute = (LPShellExecute)GetProcAddress(hInstShell32, _TWINCE("ShellExecuteW"));
#else
			pShellExecute = (LPShellExecute)GetProcAddress(hInstShell32, _TWINCE("ShellExecuteA"));
#endif
			if( pShellExecute != NULL )
			{
				if( pShellExecute( hWnd, TEXT("open"), strReadmePath, NULL, NULL, SW_SHOW ) > (HINSTANCE) 32 )
					bSuccess = true;
			}

			FreeLibrary(hInstShell32);
		}
	}

	if( !bSuccess )
	{
		// Tell the user that the readme couldn't be opened
		MessageBox( hWnd, TEXT("Could not find readme.txt"), 
			TEXT("DirectX SDK Sample"), MB_ICONWARNING | MB_OK );
	}

#endif // UNDER_CE
}





//-----------------------------------------------------------------------------
// Name: DXUtil_Trace()
// Desc: Outputs to the debug stream a formatted string with a variable-
//       argument list.
//-----------------------------------------------------------------------------
VOID DXUtil_Trace( TCHAR* strMsg, ... )
{
#if defined(DEBUG) | defined(_DEBUG)
	TCHAR strBuffer[512];

	va_list args;
	va_start(args, strMsg);
	_vsntprintf( strBuffer, 512, strMsg, args );
	va_end(args);

	OutputDebugString( strBuffer );
#else
	UNREFERENCED_PARAMETER(strMsg);
#endif
}




//-----------------------------------------------------------------------------
// Name: DXUtil_ConvertStringToGUID()
// Desc: Converts a string to a GUID
//-----------------------------------------------------------------------------
HRESULT DXUtil_ConvertStringToGUID( const TCHAR* strSrc, GUID* pGuidDest )
{
	UINT aiTmp[10];

	if( _stscanf( strSrc, TEXT("{%8X-%4X-%4X-%2X%2X-%2X%2X%2X%2X%2X%2X}"),
		&pGuidDest->Data1, 
		&aiTmp[0], &aiTmp[1], 
		&aiTmp[2], &aiTmp[3],
		&aiTmp[4], &aiTmp[5],
		&aiTmp[6], &aiTmp[7],
		&aiTmp[8], &aiTmp[9] ) != 11 )
	{
		ZeroMemory( pGuidDest, sizeof(GUID) );
		return E_FAIL;
	}
	else
	{
		pGuidDest->Data2       = (USHORT) aiTmp[0];
		pGuidDest->Data3       = (USHORT) aiTmp[1];
		pGuidDest->Data4[0]    = (BYTE) aiTmp[2];
		pGuidDest->Data4[1]    = (BYTE) aiTmp[3];
		pGuidDest->Data4[2]    = (BYTE) aiTmp[4];
		pGuidDest->Data4[3]    = (BYTE) aiTmp[5];
		pGuidDest->Data4[4]    = (BYTE) aiTmp[6];
		pGuidDest->Data4[5]    = (BYTE) aiTmp[7];
		pGuidDest->Data4[6]    = (BYTE) aiTmp[8];
		pGuidDest->Data4[7]    = (BYTE) aiTmp[9];
		return S_OK;
	}
}




//-----------------------------------------------------------------------------
// Name: DXUtil_ConvertGUIDToStringCch()
// Desc: Converts a GUID to a string 
//       cchDestChar is the size in TCHARs of strDest.  Be careful not to 
//       pass in sizeof(strDest) on UNICODE builds
//-----------------------------------------------------------------------------
HRESULT DXUtil_ConvertGUIDToStringCch( const GUID* pGuidSrc, TCHAR* strDest, int cchDestChar )
{
	int nResult = _sntprintf( strDest, cchDestChar, TEXT("{%0.8X-%0.4X-%0.4X-%0.2X%0.2X-%0.2X%0.2X%0.2X%0.2X%0.2X%0.2X}"),
		pGuidSrc->Data1, pGuidSrc->Data2, pGuidSrc->Data3,
		pGuidSrc->Data4[0], pGuidSrc->Data4[1],
		pGuidSrc->Data4[2], pGuidSrc->Data4[3],
		pGuidSrc->Data4[4], pGuidSrc->Data4[5],
		pGuidSrc->Data4[6], pGuidSrc->Data4[7] );

	if( nResult < 0 )
		return E_FAIL;
	return S_OK;
}




//-----------------------------------------------------------------------------
// Name: CArrayList constructor
// Desc: 
//-----------------------------------------------------------------------------
CArrayList::CArrayList( ArrayListType Type, UINT BytesPerEntry )
{
	if( Type == AL_REFERENCE )
		BytesPerEntry = sizeof(void*);
	m_ArrayListType = Type;
	m_pData = NULL;
	m_BytesPerEntry = BytesPerEntry;
	m_NumEntries = 0;
	m_NumEntriesAllocated = 0;
}



//-----------------------------------------------------------------------------
// Name: CArrayList destructor
// Desc: 
//-----------------------------------------------------------------------------
CArrayList::~CArrayList( void )
{
	if( m_pData != NULL )
		delete[] m_pData;
}




//-----------------------------------------------------------------------------
// Name: CArrayList::Add
// Desc: Adds pEntry to the list.
//-----------------------------------------------------------------------------
HRESULT CArrayList::Add( void* pEntry )
{
	if( m_BytesPerEntry == 0 )
		return E_FAIL;
	if( m_pData == NULL || m_NumEntries + 1 > m_NumEntriesAllocated )
	{
		void* pDataNew;
		UINT NumEntriesAllocatedNew;
		if( m_NumEntriesAllocated == 0 )
			NumEntriesAllocatedNew = 16;
		else
			NumEntriesAllocatedNew = m_NumEntriesAllocated * 2;
		pDataNew = new BYTE[NumEntriesAllocatedNew * m_BytesPerEntry];
		if( pDataNew == NULL )
			return E_OUTOFMEMORY;
		if( m_pData != NULL )
		{
			CopyMemory( pDataNew, m_pData, m_NumEntries * m_BytesPerEntry );
			delete[] m_pData;
		}
		m_pData = pDataNew;
		m_NumEntriesAllocated = NumEntriesAllocatedNew;
	}

	if( m_ArrayListType == AL_VALUE )
		CopyMemory( (BYTE*)m_pData + (m_NumEntries * m_BytesPerEntry), pEntry, m_BytesPerEntry );
	else
		*(((void**)m_pData) + m_NumEntries) = pEntry;
	m_NumEntries++;

	return S_OK;
}




//-----------------------------------------------------------------------------
// Name: CArrayList::Remove
// Desc: Remove the item at Entry in the list, and collapse the array. 
//-----------------------------------------------------------------------------
void CArrayList::Remove( UINT Entry )
{
	// Decrement count
	m_NumEntries--;

	// Find the entry address
	BYTE* pData = (BYTE*)m_pData + (Entry * m_BytesPerEntry);

	// Collapse the array
	MoveMemory( pData, pData + m_BytesPerEntry, ( m_NumEntries - Entry ) * m_BytesPerEntry );
}




//-----------------------------------------------------------------------------
// Name: CArrayList::GetPtr
// Desc: Returns a pointer to the Entry'th entry in the list.
//-----------------------------------------------------------------------------
void* CArrayList::GetPtr( UINT Entry )
{
	if( m_ArrayListType == AL_VALUE )
		return (BYTE*)m_pData + (Entry * m_BytesPerEntry);
	else
		return *(((void**)m_pData) + Entry);
}




//-----------------------------------------------------------------------------
// Name: CArrayList::Contains
// Desc: Returns whether the list contains an entry identical to the 
//       specified entry data.
//-----------------------------------------------------------------------------
bool CArrayList::Contains( void* pEntryData )
{
	for( UINT iEntry = 0; iEntry < m_NumEntries; iEntry++ )
	{
		if( m_ArrayListType == AL_VALUE )
		{
			if( memcmp( GetPtr(iEntry), pEntryData, m_BytesPerEntry ) == 0 )
				return true;
		}
		else
		{
			if( GetPtr(iEntry) == pEntryData )
				return true;
		}
	}
	return false;
}




//-----------------------------------------------------------------------------
// Name: BYTE helper functions
// Desc: cchDestChar is the size in BYTEs of strDest.  Be careful not to 
//       pass use sizeof() if the strDest is a string pointer.  
//       eg.
//       TCHAR* sz = new TCHAR[100]; // sizeof(sz)  == 4
//       TCHAR sz2[100];             // sizeof(sz2) == 200
//-----------------------------------------------------------------------------
HRESULT DXUtil_ConvertAnsiStringToWideCb( WCHAR* wstrDestination, const CHAR* strSource, int cbDestChar )
{
	return DXUtil_ConvertAnsiStringToWideCch( wstrDestination, strSource, cbDestChar / sizeof(WCHAR) );
}

HRESULT DXUtil_ConvertWideStringToAnsiCb( CHAR* strDestination, const WCHAR* wstrSource, int cbDestChar )
{
	return DXUtil_ConvertWideStringToAnsiCch( strDestination, wstrSource, cbDestChar / sizeof(CHAR) );
}

HRESULT DXUtil_ConvertGenericStringToAnsiCb( CHAR* strDestination, const TCHAR* tstrSource, int cbDestChar )
{
	return DXUtil_ConvertGenericStringToAnsiCch( strDestination, tstrSource, cbDestChar / sizeof(CHAR) );
}

HRESULT DXUtil_ConvertGenericStringToWideCb( WCHAR* wstrDestination, const TCHAR* tstrSource, int cbDestChar )
{
	return DXUtil_ConvertGenericStringToWideCch( wstrDestination, tstrSource, cbDestChar / sizeof(WCHAR) );
}

HRESULT DXUtil_ConvertAnsiStringToGenericCb( TCHAR* tstrDestination, const CHAR* strSource, int cbDestChar )
{
	return DXUtil_ConvertAnsiStringToGenericCch( tstrDestination, strSource, cbDestChar / sizeof(TCHAR) );
}

HRESULT DXUtil_ConvertWideStringToGenericCb( TCHAR* tstrDestination, const WCHAR* wstrSource, int cbDestChar )
{
	return DXUtil_ConvertWideStringToGenericCch( tstrDestination, wstrSource, cbDestChar / sizeof(TCHAR) );
}

HRESULT DXUtil_ReadStringRegKeyCb( HKEY hKey, TCHAR* strRegName, TCHAR* strDest, DWORD cbDest, TCHAR* strDefault )
{
	return DXUtil_ReadStringRegKeyCch( hKey, strRegName, strDest, cbDest / sizeof(TCHAR), strDefault );
}

HRESULT DXUtil_ConvertGUIDToStringCb( const GUID* pGuidSrc, TCHAR* strDest, int cbDestChar )
{
	return DXUtil_ConvertGUIDToStringCch( pGuidSrc, strDest, cbDestChar / sizeof(TCHAR) );
}

#ifndef UNDER_CE
HRESULT DXUtil_GetDXSDKMediaPathCb( TCHAR* szDest, int cbDest )
{
	return DXUtil_GetDXSDKMediaPathCch( szDest, cbDest / sizeof(TCHAR) );
}

HRESULT DXUtil_FindMediaFileCb( TCHAR* szDestPath, int cbDest, TCHAR* strFilename )
{
	return DXUtil_FindMediaFileCch( szDestPath, cbDest / sizeof(TCHAR), strFilename );
}
#endif // !UNDER_CE
